home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / src / rootmenu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-29  |  42.1 KB  |  1,790 lines

  1. /* rootmenu.c- user defined menu
  2.  * 
  3.  *  Window Maker window manager
  4.  * 
  5.  *  Copyright (c) 1997, 1998 Alfredo K. Kojima
  6.  *  Copyright (c) 1998       Dan Pascu
  7.  * 
  8.  *  This program is free software; you can redistribute it and/or modify
  9.  *  it under the terms of the GNU General Public License as published by
  10.  *  the Free Software Foundation; either version 2 of the License, or
  11.  *  (at your option) any later version.
  12.  *
  13.  *  This program is distributed in the hope that it will be useful,
  14.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *  GNU General Public License for more details.
  17.  *
  18.  *  You should have received a copy of the GNU General Public License
  19.  *  along with this program; if not, write to the Free Software
  20.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
  21.  *  USA.
  22.  */
  23.  
  24. #include "wconfig.h"
  25.  
  26. #ifndef LITE
  27.  
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <unistd.h>
  31. #include <sys/stat.h>
  32. #include <sys/wait.h>
  33. #include <sys/types.h>
  34. #include <string.h>
  35. #include <ctype.h>
  36. #include <time.h>
  37. #include <dirent.h>
  38.  
  39. #include <X11/Xlib.h>
  40. #include <X11/Xutil.h>
  41. #include <X11/Xatom.h>
  42.  
  43. #include "WindowMaker.h"
  44. #include "actions.h"
  45. #include "menu.h"
  46. #include "funcs.h" 
  47. #include "dialog.h"
  48. #include "keybind.h"
  49. #include "stacking.h"
  50. #include "workspace.h"
  51. #include "defaults.h"
  52. #include "framewin.h"
  53. #include "session.h"
  54. #include "xmodifier.h"
  55. #include <proplist.h>
  56.  
  57.  
  58.  
  59. extern char *Locale;
  60.  
  61. extern WDDomain *WDRootMenu;
  62.  
  63. extern Cursor wCursor[WCUR_LAST];
  64.  
  65. extern Time LastTimestamp;
  66.  
  67. extern WPreferences wPreferences;
  68.  
  69. extern int wScreenCount;
  70.  
  71. static WMenu *readMenuPipe(WScreen *scr, char **file_name);
  72. static WMenu *readMenuFile(WScreen *scr, char *file_name);
  73. static WMenu *readMenuDirectory(WScreen *scr, char *title, char **file_name,
  74.                 char *command);
  75.  
  76.  
  77. typedef struct Shortcut {
  78.     struct Shortcut *next;
  79.     
  80.     int modifier;
  81.     KeyCode keycode;
  82.     WMenuEntry *entry;
  83.     WMenu *menu;
  84. } Shortcut;
  85.  
  86.  
  87.  
  88. static Shortcut *shortcutList = NULL;
  89.  
  90.  
  91. /*
  92.  * Syntax:
  93.  * # main menu
  94.  * "Menu Name" MENU
  95.  *     "Title" EXEC command_to_exec -params
  96.  *     "Submenu" MENU
  97.  *         "Title" EXEC command_to_exec -params
  98.  *     "Submenu" END
  99.  *     "Workspaces" WORKSPACE_MENU
  100.  *     "Title" built_in_command
  101.  *     "Quit" EXIT
  102.  *     "Quick Quit" EXIT QUICK
  103.  * "Menu Name" END
  104.  * 
  105.  * Commands may be preceded by SHORTCUT key
  106.  * 
  107.  * Built-in commands:
  108.  *
  109.  * INFO_PANEL - shows the Info Panel
  110.  * LEGAL_PANEL - shows the Legal info panel
  111.  * SHUTDOWN [QUICK] - closes the X server [without confirmation]
  112.  * REFRESH - forces the desktop to be repainted
  113.  * EXIT [QUICK] - exit the window manager [without confirmation]
  114.  * EXEC <program> - execute an external program
  115.  * SHEXEC <command> - execute a shell command
  116.  * WORKSPACE_MENU - places the workspace submenu
  117.  * ARRANGE_ICONS
  118.  * RESTART [<window manager>] - restarts the window manager
  119.  * SHOW_ALL - unhide all windows on workspace
  120.  * HIDE_OTHERS - hides all windows excep the focused one
  121.  * OPEN_MENU file - read menu data from file which must be a valid menu file.
  122.  * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
  123.  *              - read menu data from directory(ies) and
  124.  *           eventually precede each with a command.
  125.  * OPEN_MENU | command
  126.  *              - opens command and uses its stdout to construct and insert
  127.  *                the resulting menu in current position. The output of
  128.  *                command must be a valid menu description.
  129.  *                The space between '|' and command is optional.
  130.  * SAVE_SESSION - saves the current state of the desktop, which include
  131.  *          all running applications, all their hints (geometry,
  132.  *          position on screen, workspace they live on, the dock
  133.  *          or clip from where they were launched, and
  134.  *          if minimized, shaded or hidden. Also saves the current
  135.  *          workspace the user is on. All will be restored on every
  136.  *          start of windowmaker until another SAVE_SESSION or
  137.  *          CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
  138.  *          WindowMaker domain file, then saving is automatically
  139.  *          done on every windowmaker exit, overwriting any
  140.  *          SAVE_SESSION or CLEAR_SESSION (see below). Also save
  141.  *          dock state now.
  142.  * CLEAR_SESSION - clears any previous saved session. This will not have
  143.  *          any effect if SaveSessionOnExit is True.
  144.  *                                 
  145.  */
  146.  
  147. #define MAX(a,b)    ((a)>(b) ? (a) : (b))
  148.  
  149.  
  150. #define M_QUICK        1
  151.  
  152. /* menu commands */
  153.  
  154. static void
  155. execCommand(WMenu *menu, WMenuEntry *entry)
  156. {
  157.     char *cmdline;
  158.  
  159.     cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
  160.  
  161.     XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
  162.          GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
  163.          CurrentTime);
  164.     XSync(dpy, 0);
  165.  
  166.     if (cmdline) {
  167.     ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
  168.     free(cmdline);
  169.     }
  170.     XUngrabPointer(dpy, CurrentTime);
  171.     XSync(dpy, 0);
  172. }
  173.  
  174.  
  175. static void
  176. exitCommand(WMenu *menu, WMenuEntry *entry)
  177. {
  178.     static int inside = 0;
  179.  
  180.     /* prevent reentrant calls */
  181.     if (inside)
  182.     return;
  183.     inside = 1;
  184.  
  185.     if ((long)entry->clientdata==M_QUICK
  186.     || wMessageDialog(menu->frame->screen_ptr, _("Exit"),
  187.               _("Exit window manager?"), 
  188.               _("Exit"), _("Cancel"), NULL)==WAPRDefault) {
  189. #ifdef DEBUG
  190.     printf("Exiting WindowMaker.\n");
  191. #endif
  192.     Shutdown(WSExitMode);
  193.     }
  194.     inside = 0;
  195. }
  196.  
  197.  
  198. static void
  199. shutdownCommand(WMenu *menu, WMenuEntry *entry)
  200. {
  201.     static int inside = 0;
  202.     int result;
  203.  
  204.     /* prevent reentrant calls */
  205.     if (inside)
  206.     return;
  207.     inside = 1;
  208.  
  209. #define R_CANCEL 0
  210. #define R_CLOSE 1
  211. #define R_KILL 2
  212.  
  213.     
  214.     result = R_CANCEL;
  215.     if ((long)entry->clientdata==M_QUICK)
  216.     result = R_CLOSE;
  217.     else {
  218. #ifdef XSMP_ENABLED
  219.     if (wSessionIsManaged()) {
  220.         int r;
  221.         
  222.         r = wMessageDialog(menu->frame->screen_ptr, 
  223.                    _("Close X session"), 
  224.                    _("Close Window System session?\n"
  225.                  "Kill might close applications with unsaved data."),
  226.                    _("Close"), _("Kill"), _("Cancel"));
  227.         if (r==WAPRDefault)
  228.         result = R_CLOSE;
  229.         else if (r==WAPRAlternate)
  230.         result = R_KILL;
  231.     } else
  232. #endif
  233.     {
  234.         int r;
  235.  
  236.         r = wMessageDialog(menu->frame->screen_ptr, 
  237.                    _("Kill X session"), 
  238.                    _("Kill Window System session?\n"
  239.                  "(all applications will be closed)"),
  240.                    _("Kill"), _("Cancel"), NULL);
  241.         if (r==WAPRDefault)
  242.         result = R_KILL;
  243.     }
  244.     } 
  245.     
  246.     if (result!=R_CANCEL) {
  247. #ifdef XSMP_ENABLED
  248.     if (result == R_CLOSE) {
  249.         Shutdown(WSLogoutMode);
  250.     } else 
  251. #endif /* XSMP_ENABLED */
  252.     {
  253.         Shutdown(WSKillMode);
  254.     }
  255.     }
  256. #undef R_CLOSE
  257. #undef R_CANCEL
  258. #undef R_KILL
  259.     inside = 0;
  260. }
  261.  
  262.  
  263. static void
  264. restartCommand(WMenu *menu, WMenuEntry *entry)
  265. {
  266.     Shutdown(WSRestartPreparationMode);
  267.     Restart((char*)entry->clientdata, False);
  268.     Restart(NULL, True);
  269. }
  270.  
  271.  
  272. static void
  273. refreshCommand(WMenu *menu, WMenuEntry *entry)
  274. {
  275.     wRefreshDesktop(menu->frame->screen_ptr);
  276. }
  277.  
  278.  
  279. static void
  280. arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
  281. {
  282.     wArrangeIcons(menu->frame->screen_ptr, True);
  283. }
  284.  
  285. static void
  286. showAllCommand(WMenu *menu, WMenuEntry *entry)
  287. {
  288.     wShowAllWindows(menu->frame->screen_ptr);
  289. }
  290.  
  291. static void
  292. hideOthersCommand(WMenu *menu, WMenuEntry *entry)
  293. {
  294.     wHideOtherApplications(menu->frame->screen_ptr->focused_window);
  295. }
  296.  
  297.  
  298. static void
  299. saveSessionCommand(WMenu *menu, WMenuEntry *entry)
  300. {
  301.     if (!wPreferences.save_session_on_exit)
  302.     wSessionSaveState(menu->frame->screen_ptr);
  303.  
  304.     wScreenSaveState(menu->frame->screen_ptr);
  305. }
  306.  
  307.  
  308. static void
  309. clearSessionCommand(WMenu *menu, WMenuEntry *entry)
  310. {
  311.     wSessionClearState(menu->frame->screen_ptr);
  312. }
  313.  
  314.  
  315. static void
  316. infoPanelCommand(WMenu *menu, WMenuEntry *entry)
  317. {
  318.     wShowInfoPanel(menu->frame->screen_ptr);
  319. }
  320.  
  321.  
  322. static void
  323. legalPanelCommand(WMenu *menu, WMenuEntry *entry)
  324. {
  325.     wShowLegalPanel(menu->frame->screen_ptr);
  326. }
  327.  
  328.  
  329. /********************************************************************/
  330.  
  331.  
  332. static char*
  333. getLocalizedMenuFile(char *menu)
  334. {
  335.     char *buffer;
  336.     char *ptr;
  337.  
  338.     if (!Locale)
  339.     return NULL;
  340.  
  341.     buffer = wmalloc(strlen(menu)+32);
  342.  
  343.     /* try menu.locale_name */
  344.     sprintf(buffer, "%s.%s", menu, Locale);
  345.     if (access(buffer, F_OK)==0) {
  346.     return buffer;
  347.     }
  348.     /* check if it is in the form aa_bb.encoding and check for aa_bb */
  349.     ptr = strchr(Locale, '.');
  350.     if (ptr) {
  351.     *ptr = 0;
  352.     if (access(buffer, F_OK)==0) {
  353.         return buffer;
  354.     }
  355.     }
  356.     /* now check for aa */
  357.     ptr = strchr(buffer, '_');
  358.     if (ptr) {
  359.     *ptr = 0;
  360.     if (access(buffer, F_OK)==0) {
  361.         return buffer;
  362.     }
  363.     }
  364.  
  365.     return NULL;
  366. }
  367.  
  368.  
  369. static void
  370. raiseMenus(WMenu *menu)
  371. {
  372.     int i;
  373.     
  374.     if (menu->flags.mapped) {
  375.     wRaiseFrame(menu->frame->core);
  376.     }
  377.     for (i=0; i<menu->cascade_no; i++) {
  378.     if (menu->cascades[i])
  379.       raiseMenus(menu->cascades[i]);
  380.     }
  381. }
  382.  
  383.  
  384.  
  385. Bool
  386. wRootMenuPerformShortcut(XEvent *event)
  387. {
  388.     Shortcut *ptr;
  389.     int modifiers;
  390.     int done = 0;
  391.     
  392.     /* ignore CapsLock */
  393.     modifiers = event->xkey.state & ValidModMask;
  394.     
  395.     for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
  396.     if (ptr->keycode==0) 
  397.         continue;
  398.     
  399.     if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
  400.         (*ptr->entry->callback)(ptr->menu, ptr->entry);
  401.         done = True;
  402.     }
  403.     }
  404.     return done;
  405. }
  406.  
  407.  
  408. void
  409. wRootMenuBindShortcuts(Window window)
  410. {
  411.     Shortcut *ptr;
  412.     
  413.     ptr = shortcutList;
  414.     while (ptr) {
  415.     if (ptr->modifier!=AnyModifier) {
  416.         XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
  417.              window, True, GrabModeAsync, GrabModeAsync);
  418. #ifdef NUMLOCK_HACK
  419.         wHackedGrabKey(ptr->keycode, ptr->modifier,
  420.                window, True, GrabModeAsync, GrabModeAsync);        
  421. #endif
  422.     }
  423.     XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, 
  424.          GrabModeAsync, GrabModeAsync);
  425.     ptr = ptr->next;
  426.     }
  427. }
  428.  
  429.  
  430. static void
  431. rebindKeygrabs(WScreen *scr)
  432. {
  433.     WWindow *wwin;
  434.     
  435.     wwin = scr->focused_window;
  436.     
  437.     while (wwin!=NULL) {
  438.     XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
  439.  
  440.     if (!WFLAGP(wwin, no_bind_keys)) {
  441.         wWindowSetKeyGrabs(wwin);
  442.     }
  443.     wwin = wwin->prev;
  444.     }
  445. }
  446.  
  447.  
  448. static void
  449. removeShortcutsForMenu(WMenu *menu)
  450. {
  451.     Shortcut *ptr, *tmp;
  452.     Shortcut *newList = NULL;
  453.  
  454.     ptr = shortcutList;
  455.     while (ptr!=NULL) {
  456.     tmp = ptr->next;
  457.     if (ptr->menu == menu) {
  458.         free(ptr);
  459.     } else {
  460.         ptr->next = newList;
  461.         newList = ptr;
  462.     }
  463.     ptr = tmp;
  464.     }
  465.     shortcutList = newList;
  466.     menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
  467. }
  468.  
  469.  
  470. static Bool
  471. addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
  472.         WMenuEntry *entry)
  473. {
  474.     Shortcut *ptr;    
  475.     KeySym ksym;
  476.     char *k;
  477.     char buf[128], *b;
  478.  
  479.     ptr = wmalloc(sizeof(Shortcut));
  480.     
  481.     strcpy(buf, shortcutDefinition);
  482.     b = (char*)buf;
  483.  
  484.     /* get modifiers */
  485.     ptr->modifier = 0;
  486.     while ((k = strchr(b, '+'))!=NULL) {
  487.     int mod;
  488.     
  489.     *k = 0;
  490.     mod = wXModifierFromKey(b);
  491.     if (mod<0) {
  492.         wwarning(_("%s:invalid key modifier \"%s\""), file, b);
  493.         free(ptr);
  494.         return False;
  495.     }
  496.     ptr->modifier |= mod;
  497.     
  498.     b = k+1;
  499.     }
  500.     
  501.     /* get key */
  502.     ksym = XStringToKeysym(b);
  503.     
  504.     if (ksym==NoSymbol) {
  505.     wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
  506.          file, shortcutDefinition, entry->text);
  507.     free(ptr);
  508.     return False;
  509.     }
  510.     
  511.     ptr->keycode = XKeysymToKeycode(dpy, ksym);
  512.     if (ptr->keycode==0) {
  513.     wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
  514.          shortcutDefinition, entry->text);
  515.     free(ptr);
  516.     return False;
  517.     }
  518.  
  519.     ptr->menu = menu;
  520.     ptr->entry = entry;
  521.     
  522.     ptr->next = shortcutList;
  523.     shortcutList = ptr;
  524.     
  525.     menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
  526.     
  527.     return True;
  528. }
  529.  
  530.  
  531. /*******************************/
  532.  
  533. static char*
  534. cropline(char *line)
  535. {
  536.     char *end;
  537.     
  538.     if (strlen(line)==0)
  539.     return line;
  540.     
  541.     end = &(line[strlen(line)])-1;
  542.     while (isspace(*line) && *line!=0) line++;
  543.     while (end>line && isspace(*end)) {
  544.     *end=0;
  545.     end--;
  546.     }
  547.     return line;
  548. }
  549.  
  550.  
  551. static  char*
  552. next_token(char *line, char **next)
  553. {
  554.     char *tmp, c;
  555.     char *ret;
  556.  
  557.     *next = NULL;
  558.     while (*line==' ' || *line=='\t') line++;
  559.  
  560.     tmp = line;
  561.  
  562.     if (*tmp=='"') {
  563.         tmp++; line++;
  564.         while (*tmp!=0 && *tmp!='"') tmp++;
  565.         if (*tmp!='"') {
  566.             wwarning(_("%s: unmatched '\"' in menu file"), line);
  567.             return NULL;
  568.         }
  569.     } else {
  570.         do {
  571.             if (*tmp=='\\')
  572.                 tmp++;
  573.  
  574.             if (*tmp!=0)
  575.                 tmp++;
  576.  
  577.         } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
  578.     }
  579.  
  580.     c = *tmp;
  581.     *tmp = 0;
  582.     ret = wstrdup(line);
  583.     *tmp = c;
  584.  
  585.     if (c==0)
  586.         return ret;
  587.     else
  588.         tmp++;
  589.  
  590.     /* skip blanks */
  591.     while (*tmp==' ' || *tmp=='\t') tmp++;
  592.  
  593.     if (*tmp!=0)
  594.         *next = tmp;
  595.  
  596.     return ret;
  597. }
  598.  
  599.  
  600. static void
  601. separateCommand(char *line, char ***file, char **command)
  602. {
  603.     char *token, *tmp = line;
  604.     WMBag *bag = WMCreateBag(4);
  605.     int count, i;
  606.  
  607.     *file = NULL;
  608.     *command = NULL;
  609.     do {
  610.         token = next_token(tmp, &tmp);
  611.         if (token) {
  612.             if (strcmp(token, "WITH")==0) {
  613.                 if (tmp!=NULL && *tmp!=0)
  614.                     *command = wstrdup(tmp);
  615.                 else
  616.                     wwarning(_("%s: missing command"), line);
  617.                 break;
  618.             }
  619.         WMPutInBag(bag, token);
  620.         }
  621.     } while (token!=NULL && tmp!=NULL);
  622.  
  623.     count = WMGetBagItemCount(bag);
  624.     if (count>0) {
  625.         *file = wmalloc(sizeof(char*)*(count+1));
  626.         (*file)[count] = NULL;
  627.         for (i = 0; i < count; i++) {
  628.             (*file)[i] = WMGetFromBag(bag, i);
  629.         }
  630.     }
  631.     WMFreeBag(bag);
  632. }
  633.  
  634.  
  635. static void
  636. constructMenu(WMenu *menu, WMenuEntry *entry)
  637. {
  638.     WMenu *submenu;
  639.     struct stat stat_buf;
  640.     char **path;
  641.     char *cmd;
  642.     char *lpath = NULL;
  643.     int i, first=-1;
  644.     time_t last=0;
  645.     
  646.     separateCommand((char*)entry->clientdata, &path, &cmd);
  647.     if (path == NULL || *path==NULL || **path==0) {
  648.     wwarning(_("invalid OPEN_MENU specification: %s"),
  649.          (char*)entry->clientdata);
  650.     return;
  651.     }
  652.     
  653.     if (path[0][0]=='|') {
  654.         /* pipe menu */
  655.     
  656.         if (!menu->cascades[entry->cascade] || 
  657.             menu->cascades[entry->cascade]->timestamp == 0) { 
  658.             /* parse pipe */
  659.  
  660.         submenu = readMenuPipe(menu->frame->screen_ptr, path);
  661.  
  662.             /* there's no automatic reloading */
  663.             if(submenu != NULL)
  664.                 submenu->timestamp = 1;
  665.         } else { 
  666.             submenu = NULL; 
  667.         }
  668.   
  669.     } else {
  670.         i=0;
  671.         while(path[i] != NULL) {
  672.         char *tmp;
  673.  
  674.         if (strcmp(path[i], "-noext")==0) {
  675.         i++;
  676.         continue;
  677.         }
  678.         
  679.         tmp = wexpandpath(path[i]);
  680.         free(path[i]);
  681.         lpath = getLocalizedMenuFile(tmp);
  682.         if (lpath) {
  683.         free(tmp);
  684.         path[i] = lpath;
  685.         lpath = NULL;
  686.         } else {
  687.         path[i] = tmp;
  688.         }
  689.  
  690.             if (stat(path[i], &stat_buf)==0) {
  691.                 if (last < stat_buf.st_mtime)
  692.                     last = stat_buf.st_mtime;
  693.                 if (first<0)
  694.                     first=i;
  695.             } else {
  696.                 wsyserror(_("%s:could not stat menu"), path[i]);
  697.                 /*goto finish;*/
  698.             }
  699.  
  700.             i++;
  701.         }
  702.  
  703.         if (first < 0) {
  704.             wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
  705.                       (char*)entry->clientdata);
  706.             goto finish;
  707.         }
  708.         stat(path[first], &stat_buf);
  709.     if (!menu->cascades[entry->cascade]
  710.         || menu->cascades[entry->cascade]->timestamp < last) {
  711.  
  712.         if (S_ISDIR(stat_buf.st_mode)) {
  713.         /* menu directory */
  714.         submenu = readMenuDirectory(menu->frame->screen_ptr,
  715.                                             entry->text, path, cmd);
  716.         if (submenu)
  717.             submenu->timestamp = last;
  718.         } else if (S_ISREG(stat_buf.st_mode)) {
  719.         /* menu file */
  720.  
  721.         if (cmd || path[1])
  722.             wwarning(_("too many parameters in OPEN_MENU: %s"), 
  723.                  (char*)entry->clientdata);
  724.  
  725.         submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
  726.         if (submenu)
  727.             submenu->timestamp = stat_buf.st_mtime;
  728.         } else {
  729.         submenu = NULL;
  730.         }
  731.     } else {
  732.         submenu = NULL;
  733.     }
  734.     }
  735.  
  736.     if (submenu) {
  737.     wMenuEntryRemoveCascade(menu, entry);
  738.     wMenuEntrySetCascade(menu, entry, submenu);
  739.     }
  740.  
  741. finish:
  742.     i = 0;
  743.     while (path[i]!=NULL)
  744.         free(path[i++]);
  745.     free(path);
  746.     if (cmd)
  747.     free(cmd);
  748. }
  749.  
  750.  
  751. static WMenuEntry*
  752. addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
  753. {
  754.     WMenu *wsmenu;
  755.     WMenuEntry *entry;
  756.  
  757.     if (scr->flags.added_workspace_menu) {
  758.     wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
  759.     return NULL;
  760.     } else {
  761.     scr->flags.added_workspace_menu = 1;
  762.     
  763.     wsmenu = wWorkspaceMenuMake(scr, True);
  764.     scr->workspace_menu = wsmenu;
  765.     entry = wMenuAddCallback(menu, title, NULL, NULL);
  766.     wMenuEntrySetCascade(menu, entry, wsmenu);
  767.     
  768.     wWorkspaceMenuUpdate(scr, wsmenu);
  769.     }
  770.     return entry;
  771. }
  772.  
  773.  
  774. static WMenuEntry*
  775. addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
  776.          char *params, char *file_name)
  777. {
  778.     WScreen *scr;
  779.     WMenuEntry *entry = NULL;
  780.     Bool shortcutOk = False;
  781.  
  782.     if (!menu)
  783.     return NULL;
  784.     scr = menu->frame->screen_ptr;
  785.     if (strcmp(command, "OPEN_MENU")==0) {
  786.     if (!params) {
  787.         wwarning(_("%s:missing parameter for menu command \"%s\""),
  788.              file_name, command);
  789.     } else {
  790.         WMenu *dummy;
  791.         char *path;
  792.         
  793.             path = wfindfile(DEF_CONFIG_PATHS, params);
  794.         if (!path) {
  795.         path = wstrdup(params);
  796.         }
  797.         dummy = wMenuCreate(scr, title, False);
  798.         dummy->on_destroy = removeShortcutsForMenu;
  799.         entry = wMenuAddCallback(menu, title, constructMenu, path);
  800.         entry->free_cdata = free;
  801.         wMenuEntrySetCascade(menu, entry, dummy);
  802.     }
  803.     } else if (strcmp(command, "EXEC")==0) {
  804.     if (!params)
  805.         wwarning(_("%s:missing parameter for menu command \"%s\""),
  806.              file_name, command);
  807.     else {
  808.         entry = wMenuAddCallback(menu, title, execCommand, 
  809.                      wstrappend("exec ", params));
  810.         entry->free_cdata = free;
  811.         shortcutOk = True;
  812.     }
  813.     } else if (strcmp(command, "SHEXEC")==0) {
  814.     if (!params)
  815.         wwarning(_("%s:missing parameter for menu command \"%s\""),
  816.              file_name, command);
  817.     else {
  818.         entry = wMenuAddCallback(menu, title, execCommand,
  819.                      wstrdup(params));
  820.         entry->free_cdata = free;
  821.         shortcutOk = True;
  822.     }
  823.     } else if (strcmp(command, "EXIT")==0) {
  824.     
  825.     if (params && strcmp(params, "QUICK")==0)
  826.         entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
  827.     else
  828.         entry = wMenuAddCallback(menu, title, exitCommand, NULL);
  829.  
  830.     shortcutOk = True;
  831.     } else if (strcmp(command, "SHUTDOWN")==0) {
  832.         
  833.     if (params && strcmp(params, "QUICK")==0)
  834.         entry = wMenuAddCallback(menu, title, shutdownCommand, 
  835.                      (void*)M_QUICK);
  836.     else
  837.         entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
  838.     
  839.     shortcutOk = True;
  840.     } else if (strcmp(command, "REFRESH")==0) {
  841.     entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
  842.     
  843.     shortcutOk = True;
  844.     } else if (strcmp(command, "WORKSPACE_MENU")==0) {
  845.     entry = addWorkspaceMenu(scr, menu, title);
  846.     
  847.     shortcutOk = True;
  848.     } else if (strcmp(command, "ARRANGE_ICONS")==0) {
  849.     entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
  850.     
  851.     shortcutOk = True;
  852.     } else if (strcmp(command, "HIDE_OTHERS")==0) {
  853.     entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
  854.     
  855.     shortcutOk = True;
  856.     } else if (strcmp(command, "SHOW_ALL")==0) {
  857.     entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
  858.     
  859.     shortcutOk = True;
  860.     } else if (strcmp(command, "RESTART")==0) {
  861.     entry = wMenuAddCallback(menu, title, restartCommand, 
  862.                  params ? wstrdup(params) : NULL);
  863.     entry->free_cdata = free;
  864.     shortcutOk = True;
  865.     } else if (strcmp(command, "SAVE_SESSION")==0) {
  866.         entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
  867.         
  868.     shortcutOk = True;
  869.     } else if (strcmp(command, "CLEAR_SESSION")==0) {
  870.         entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
  871.     shortcutOk = True;
  872.     } else if (strcmp(command, "INFO_PANEL")==0) {
  873.     entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
  874.     shortcutOk = True;
  875.     } else if (strcmp(command, "LEGAL_PANEL")==0) {
  876.     entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
  877.     shortcutOk = True;
  878.     } else {
  879.     wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
  880.          command);
  881.     
  882.     return NULL;
  883.     }
  884.     
  885.     if (shortcut && entry) {
  886.     if (!shortcutOk) {
  887.         wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
  888.              title);
  889.     } else {
  890.         if (addShortcut(file_name, shortcut, menu, entry)) {
  891.           
  892.         entry->rtext = GetShortcutString(shortcut);
  893.          /*
  894.         entry->rtext = wstrdup(shortcut);
  895.           */
  896.         }
  897.     }
  898.     }
  899.  
  900.     return entry;
  901. }
  902.  
  903.  
  904.  
  905. /*******************   Menu Configuration From File   *******************/
  906.  
  907. static void
  908. separateline(char *line, char *title, char *command, char *parameter,
  909.          char *shortcut)
  910. {
  911.     int l, i;
  912.     
  913.     l = strlen(line);
  914.  
  915.     *title = 0;
  916.     *command = 0;
  917.     *parameter = 0;
  918.     *shortcut = 0;
  919.     /* get the title */
  920.     while (isspace(*line) && (*line!=0)) line++;
  921.     if (*line=='"') {
  922.     line++;
  923.     i=0;
  924.     while (line[i]!='"' && (line[i]!=0)) i++;
  925.     if (line[i]!='"')
  926.       return;
  927.     } else {
  928.     i=0;
  929.     while (!isspace(line[i]) && (line[i]!=0)) i++;
  930.     }
  931.     strncpy(title, line, i);
  932.     title[i++]=0;
  933.     line+=i;
  934.  
  935.     /* get the command or shortcut keyword */
  936.     while (isspace(*line) && (*line!=0)) line++;
  937.     if (*line==0)
  938.       return;
  939.     i=0;
  940.     while (!isspace(line[i]) && (line[i]!=0)) i++;
  941.     strncpy(command, line, i);
  942.     command[i++]=0;
  943.     line+=i;
  944.  
  945.     if (strcmp(command, "SHORTCUT")==0) {
  946.     /* get the shortcut key */
  947.     while (isspace(*line) && (*line!=0)) line++;
  948.     if (*line=='"') {
  949.         line++;
  950.         i=0;
  951.         while (line[i]!='"' && (line[i]!=0)) i++;
  952.         if (line[i]!='"')
  953.         return;
  954.     } else {
  955.         i=0;
  956.         while (!isspace(line[i]) && (line[i]!=0)) i++;
  957.     }
  958.     strncpy(shortcut, line, i);
  959.     shortcut[i++]=0;
  960.     line+=i;
  961.     
  962.     *command=0;
  963.     
  964.     /* get the command */
  965.     while (isspace(*line) && (*line!=0)) line++;
  966.     if (*line==0)
  967.         return;
  968.     i=0;
  969.     while (!isspace(line[i]) && (line[i]!=0)) i++;
  970.     strncpy(command, line, i);
  971.     command[i++]=0;
  972.     line+=i;
  973.     }
  974.  
  975.     /* get the parameters */
  976.     while (isspace(*line) && (*line!=0)) line++;
  977.     if (*line==0)
  978.       return;
  979.     
  980.     if (*line=='"') {
  981.     line++;
  982.     l = 0;
  983.     while (line[l]!=0 && line[l]!='"') {
  984.         parameter[l] = line[l];
  985.         l++;
  986.     }
  987.     parameter[l] = 0;
  988.     return;
  989.     }
  990.     
  991.     l = strlen(line);
  992.     while (isspace(line[l]) && (l>0)) l--;
  993.     strncpy(parameter, line, l);
  994.     parameter[l]=0;
  995. }
  996.  
  997.  
  998. static WMenu*
  999. parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
  1000. {
  1001.     char linebuf[MAXLINE];
  1002.     char elinebuf[MAXLINE];
  1003.     char title[MAXLINE];
  1004.     char command[MAXLINE];
  1005.     char shortcut[MAXLINE];
  1006.     char params[MAXLINE];
  1007.     char *line;
  1008.     
  1009.     while (!IsEof(file)) {
  1010.     int lsize, ok;
  1011.     
  1012.     ok = 0;
  1013.     fgets(linebuf, MAXLINE, file);
  1014.     line = cropline(linebuf);
  1015.     lsize = strlen(line);
  1016.     do {
  1017.         if (line[lsize-1]=='\\') {
  1018.         char *line2;
  1019.         int lsize2;
  1020.         fgets(elinebuf, MAXLINE, file);
  1021.         line2=cropline(elinebuf);
  1022.         lsize2=strlen(line2);
  1023.         if (lsize2+lsize>MAXLINE) {
  1024.             wwarning(_("%s:maximal line size exceeded in menu config: %s"),
  1025.                  file_name, line);
  1026.             ok=2;
  1027.         } else {
  1028.             line[lsize-1]=0;
  1029.             lsize+=lsize2-1;
  1030.             strcat(line, line2);
  1031.         }
  1032.         } else {
  1033.         ok=1;
  1034.         }
  1035.     } while (!ok && !IsEof(file));
  1036.     if (ok==2)
  1037.       continue;
  1038.     
  1039.     if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) 
  1040.       continue;
  1041.  
  1042.     
  1043.     separateline(line, title, command, params, shortcut);
  1044.     
  1045.     if (!command[0]) {
  1046.         wwarning(_("%s:missing command in menu config: %s"), file_name,
  1047.              line);
  1048.         goto error;
  1049.     }
  1050.  
  1051.     if (strcasecmp(command, "MENU")==0) {
  1052.         WMenu *cascade;
  1053.         
  1054.         /* start submenu */
  1055.  
  1056.         cascade = wMenuCreate(scr, title, False);
  1057.         cascade->on_destroy = removeShortcutsForMenu;
  1058.         if (parseCascade(scr, cascade, file, file_name)==NULL) {
  1059.         wMenuDestroy(cascade, True);
  1060.         } else {
  1061.         wMenuEntrySetCascade(menu, 
  1062.                      wMenuAddCallback(menu, title, NULL, NULL),
  1063.                      cascade);
  1064.         }
  1065.     } else if (strcasecmp(command, "END")==0) {
  1066.         /* end of menu */
  1067.         return menu;
  1068.         
  1069.     } else {
  1070.         /* normal items */
  1071.         addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command, 
  1072.              params[0] ? params : NULL, file_name);
  1073.     }
  1074.     }
  1075.  
  1076.     wwarning(_("%s:syntax error in menu file:END declaration missing"),
  1077.          file_name);
  1078.     return menu;
  1079.  
  1080.     error:
  1081.     return menu;
  1082. }
  1083.  
  1084.  
  1085. static WMenu*
  1086. readMenuFile(WScreen *scr, char *file_name)
  1087. {
  1088.     WMenu *menu=NULL;
  1089.     FILE *file = NULL;
  1090.     char linebuf[MAXLINE];
  1091.     char title[MAXLINE];
  1092.     char shortcut[MAXLINE];
  1093.     char command[MAXLINE];
  1094.     char params[MAXLINE];
  1095.     char *line;
  1096. #ifdef USECPP
  1097.     char *args;
  1098.     int cpp = 0;
  1099. #endif
  1100.  
  1101. #ifdef USECPP
  1102.     if (!wPreferences.flags.nocpp) {
  1103.     args = MakeCPPArgs(file_name);
  1104.     if (!args) {
  1105.         wwarning(_("could not make arguments for menu file preprocessor"));
  1106.     } else {
  1107.         sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
  1108.         free(args);
  1109.         file = popen(command, "r");
  1110.         if (!file) {
  1111.         wsyserror(_("%s:could not open/preprocess menu file"), 
  1112.               file_name);
  1113.         } else {
  1114.         cpp = 1;
  1115.         }
  1116.     }
  1117.     }
  1118. #endif /* USECPP */
  1119.  
  1120.     if (!file) {
  1121.     file = fopen(file_name, "r");
  1122.     if (!file) {
  1123.         wsyserror(_("%s:could not open menu file"), file_name);
  1124.         return NULL;
  1125.     }
  1126.     }
  1127.     
  1128.     while (!IsEof(file)) {
  1129.     if (!fgets(linebuf, MAXLINE, file))
  1130.         break;
  1131.     line = cropline(linebuf);
  1132.     if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) 
  1133.       continue;
  1134.  
  1135.     separateline(line, title, command, params, shortcut);
  1136.     
  1137.     if (!command[0]) {
  1138.         wwarning(_("%s:missing command in menu config: %s"), file_name,
  1139.              line);
  1140.         break;
  1141.     }
  1142.     if (strcasecmp(command, "MENU")==0) {
  1143.         menu = wMenuCreate(scr, title, True);
  1144.         menu->on_destroy = removeShortcutsForMenu;
  1145.         if (!parseCascade(scr, menu, file, file_name)) {
  1146.         wMenuDestroy(menu, True);
  1147.         }
  1148.         break;
  1149.     } else {
  1150.         wwarning(_("%s:invalid menu file. MENU command is missing"), 
  1151.              file_name);
  1152.         break;
  1153.     }
  1154.     }
  1155.     
  1156. #ifdef CPP
  1157.     if (cpp) {
  1158.     if (pclose(file)==-1) {
  1159.         wsyserror(_("error reading preprocessed menu data"));
  1160.     }
  1161.     } else {
  1162.     fclose(file);
  1163.     }
  1164. #else
  1165.     fclose(file);
  1166. #endif
  1167.     
  1168.     return menu;
  1169. }
  1170.  
  1171. /************    Menu Configuration From Pipe      *************/
  1172.  
  1173. static WMenu*
  1174. readMenuPipe(WScreen *scr, char **file_name) 
  1175. {
  1176.     WMenu *menu=NULL;
  1177.     FILE *file = NULL;
  1178.     char linebuf[MAXLINE];
  1179.     char title[MAXLINE];
  1180.     char command[MAXLINE];
  1181.     char params[MAXLINE];
  1182.     char shortcut[MAXLINE];
  1183.     char *line;
  1184.     char * filename;
  1185.     char flat_file[MAXLINE];
  1186.     int i;
  1187. #ifdef USECPP
  1188.     char *args;
  1189.     int cpp = 0;
  1190. #endif
  1191.  
  1192.     flat_file[0] = '\0';
  1193.  
  1194.     for(i = 0 ; file_name[i] != NULL ; i++) {
  1195.     strcat(flat_file, file_name[i]);
  1196.     strcat(flat_file, " ");
  1197.     }
  1198.     filename = flat_file+1;
  1199.  
  1200. #ifdef USECPP
  1201.     if (!wPreferences.flags.nocpp) {
  1202.        args = MakeCPPArgs(filename);
  1203.        if (!args) {
  1204.            wwarning(_("could not make arguments for menu file preprocessor"));
  1205.        } else {
  1206.            sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
  1207.  
  1208.            free(args);
  1209.            file = popen(command, "r");
  1210.            if (!file) {
  1211.                wsyserror(_("%s:could not open/preprocess menu file"), filename);
  1212.            } else {
  1213.                cpp = 1;
  1214.            }
  1215.        }
  1216.     }
  1217.  
  1218. #endif /* USECPP */
  1219.  
  1220.     if (!file) {
  1221.        file = popen(filename, "r");
  1222.  
  1223.        if (!file) {
  1224.            wsyserror(_("%s:could not open menu file"), filename);
  1225.            return NULL;
  1226.        }
  1227.     }
  1228.  
  1229.     while (!IsEof(file)) {
  1230.     if (!fgets(linebuf, MAXLINE, file))
  1231.         break;
  1232.     line = cropline(linebuf);
  1233.     if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) 
  1234.         continue;
  1235.  
  1236.     separateline(line, title, command, params, shortcut);
  1237.  
  1238.     if (!command[0]) {
  1239.         wwarning(_("%s:missing command in menu config: %s"), file_name,
  1240.              line);
  1241.         break;
  1242.     }
  1243.     if (strcasecmp(command, "MENU")==0) {
  1244.         menu = wMenuCreate(scr, title, True);
  1245.         menu->on_destroy = removeShortcutsForMenu;
  1246.         if (!parseCascade(scr, menu, file, filename)) {
  1247.         wMenuDestroy(menu, True);
  1248.         }
  1249.         break;
  1250.     } else {
  1251.         wwarning(_("%s:no title given for the root menu"), filename);
  1252.         break;
  1253.     }
  1254.     }
  1255.  
  1256.     pclose(file);
  1257.  
  1258.     return menu;
  1259. }
  1260.  
  1261.  
  1262.  
  1263. typedef struct {
  1264.     char *name;
  1265.     int index;
  1266. } dir_data;
  1267.  
  1268.  
  1269. static int
  1270. myCompare(const void *d1, const void *d2)
  1271. {
  1272.     dir_data *p1 = *(dir_data**)d1;
  1273.     dir_data *p2 = *(dir_data**)d2;
  1274.  
  1275.     return strcmp(p1->name, p2->name);
  1276. }
  1277.  
  1278.  
  1279. /************  Menu Configuration From Directory   *************/
  1280.  
  1281. static Bool
  1282. isFilePackage(char *file)
  1283. {
  1284.     int l;
  1285.  
  1286.     /* check if the extension indicates this file is a 
  1287.      * file package. For now, only recognize .themed */
  1288.  
  1289.     l = strlen(file);
  1290.  
  1291.     if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
  1292.     return True;
  1293.     } else {
  1294.     return False;
  1295.     }
  1296. }
  1297.  
  1298.  
  1299. static WMenu*
  1300. readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
  1301. {
  1302.     DIR *dir;
  1303.     struct dirent *dentry;
  1304.     struct stat stat_buf;
  1305.     WMenu *menu=NULL;
  1306.     char *buffer;
  1307.     WMBag *dirs = NULL, *files = NULL;
  1308.     int length, i, have_space=0;
  1309.     dir_data *data;
  1310.     int stripExtension = 0;
  1311.  
  1312.     
  1313.     dirs = WMCreateBag(16);
  1314.     files = WMCreateBag(16);
  1315.     
  1316.     i=0;
  1317.     while (path[i]!=NULL) {
  1318.     if (strcmp(path[i], "-noext")==0) {
  1319.         stripExtension = 1;
  1320.         i++;
  1321.         continue;
  1322.     }
  1323.  
  1324.         dir = opendir(path[i]);
  1325.         if (!dir) {
  1326.             i++;
  1327.             continue;
  1328.         }
  1329.  
  1330.         while ((dentry = readdir(dir))) {
  1331.  
  1332.             if (strcmp(dentry->d_name, ".")==0 ||
  1333.                 strcmp(dentry->d_name, "..")==0)
  1334.                 continue;
  1335.  
  1336.         if (dentry->d_name[0] == '.')
  1337.         continue;
  1338.  
  1339.             buffer = malloc(strlen(path[i])+strlen(dentry->d_name)+4);
  1340.             if (!buffer) {
  1341.                 wsyserror(_("out of memory while constructing directory menu %s"),
  1342.                          path[i]);
  1343.                 break;
  1344.             }
  1345.  
  1346.             strcpy(buffer, path[i]);
  1347.             strcat(buffer, "/");
  1348.             strcat(buffer, dentry->d_name);
  1349.  
  1350.             if (stat(buffer, &stat_buf)!=0) {
  1351.                 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
  1352.                           path[i], dentry->d_name);
  1353.             } else {
  1354.         Bool isFilePack = False;
  1355.  
  1356.         data = NULL;
  1357.                 if (S_ISDIR(stat_buf.st_mode)
  1358.             && !(isFilePack = isFilePackage(dentry->d_name))) {
  1359.  
  1360.                     /* access always returns success for user root */
  1361.                     if (access(buffer, X_OK)==0) {
  1362.                         /* Directory is accesible. Add to directory list */
  1363.  
  1364.             data = (dir_data*) wmalloc(sizeof(dir_data));
  1365.             data->name = wstrdup(dentry->d_name);
  1366.             data->index = i;
  1367.  
  1368.             WMPutInBag(dirs, data);
  1369.                     }
  1370.                 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
  1371.                     /* Hack because access always returns X_OK success for user root */
  1372. #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
  1373.                     if ((command!=NULL && access(buffer, R_OK)==0) ||
  1374.                         (command==NULL && access(buffer, X_OK)==0 &&
  1375.                          (stat_buf.st_mode & S_IXANY))) {
  1376.  
  1377.             data = (dir_data*) wmalloc(sizeof(dir_data));
  1378.             data->name = wstrdup(dentry->d_name);
  1379.             data->index = i;
  1380.  
  1381.             WMPutInBag(files, data);
  1382.                     }
  1383.                 }
  1384.             }
  1385.             free(buffer);
  1386.         }
  1387.  
  1388.         closedir(dir);
  1389.         i++;
  1390.     }
  1391.     
  1392.     if (!WMGetBagItemCount(dirs) && !WMGetBagItemCount(files)) {
  1393.     WMFreeBag(dirs);
  1394.     WMFreeBag(files);
  1395.         return NULL;
  1396.     }
  1397.  
  1398.     WMSortBag(dirs, myCompare);
  1399.     WMSortBag(files, myCompare);
  1400.  
  1401.     menu = wMenuCreate(scr, title, False);
  1402.     menu->on_destroy = removeShortcutsForMenu;
  1403.  
  1404.     for (i = 0; i < WMGetBagItemCount(dirs); i++) {
  1405.         /* New directory. Use same OPEN_MENU command that was used
  1406.          * for the current directory. */
  1407.         dir_data *d = (dir_data*)WMGetFromBag(dirs, i);
  1408.  
  1409.         length = strlen(path[d->index])+strlen(d->name)+6;
  1410.     if (stripExtension)
  1411.         length += 7;
  1412.         if (command)
  1413.             length += strlen(command) + 6;
  1414.         buffer = malloc(length);
  1415.         if (!buffer) {
  1416.             wsyserror(_("out of memory while constructing directory menu %s"),
  1417.                      path[d->index]);
  1418.             break;
  1419.         }
  1420.     
  1421.     buffer[0] = '\0';
  1422.         if (stripExtension)
  1423.             strcat(buffer, "-noext ");
  1424.  
  1425.         have_space = strchr(path[d->index], ' ')!=NULL ||
  1426.                      strchr(d->name, ' ')!=NULL;
  1427.  
  1428.     if (have_space)
  1429.             strcat(buffer, "\"");
  1430.     strcat(buffer, path[d->index]);
  1431.  
  1432.         strcat(buffer, "/");
  1433.         strcat(buffer, d->name);
  1434.         if (have_space)
  1435.             strcat(buffer, "\"");
  1436.         if (command) {
  1437.             strcat(buffer, " WITH ");
  1438.             strcat(buffer, command);
  1439.         }
  1440.  
  1441.         addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
  1442.  
  1443.         free(buffer);
  1444.     if (d->name)
  1445.         free(d->name);
  1446.     free(d);
  1447.     }
  1448.  
  1449.     for (i = 0; i < WMGetBagItemCount(files); i++) {
  1450.         /* executable: add as entry */
  1451.         dir_data *f = (dir_data*)WMGetFromBag(files, i);
  1452.  
  1453.         length = strlen(path[f->index])+strlen(f->name)+6;
  1454.         if (command)
  1455.             length += strlen(command);
  1456.  
  1457.         buffer = malloc(length);
  1458.         if (!buffer) {
  1459.             wsyserror(_("out of memory while constructing directory menu %s"),
  1460.                      path[f->index]);
  1461.             break;
  1462.         }
  1463.  
  1464.         have_space = strchr(path[f->index], ' ')!=NULL ||
  1465.                      strchr(f->name, ' ')!=NULL;
  1466.         if (command!=NULL) {
  1467.             strcpy(buffer, command);
  1468.             strcat(buffer, " ");
  1469.             if (have_space)
  1470.                 strcat(buffer, "\"");
  1471.             strcat(buffer, path[f->index]);
  1472.         } else {
  1473.             if (have_space) {
  1474.                 buffer[0] = '"';
  1475.                 buffer[1] = 0;
  1476.                 strcat(buffer, path[f->index]);
  1477.             } else {
  1478.                 strcpy(buffer, path[f->index]);
  1479.             }
  1480.         }
  1481.         strcat(buffer, "/");
  1482.         strcat(buffer, f->name);
  1483.         if (have_space)
  1484.             strcat(buffer, "\"");
  1485.  
  1486.     if (stripExtension) {
  1487.         char *ptr = strrchr(f->name, '.');
  1488.         if (ptr && ptr!=f->name)
  1489.         *ptr = 0;
  1490.     }
  1491.         addMenuEntry(menu, f->name, NULL, "SHEXEC", buffer, path[f->index]);
  1492.  
  1493.         free(buffer);
  1494.     if (f->name)
  1495.         free(f->name);
  1496.     free(f);
  1497.     }
  1498.     
  1499.     WMFreeBag(files);
  1500.     WMFreeBag(dirs);
  1501.  
  1502.     return menu;
  1503. }
  1504.  
  1505.  
  1506. /************  Menu Configuration From WMRootMenu   *************/
  1507.  
  1508. static WMenu*
  1509. makeDefaultMenu(WScreen *scr)
  1510. {
  1511.     WMenu *menu=NULL;
  1512.     
  1513.     menu = wMenuCreate(scr, _("Commands"), True);
  1514.     wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
  1515.     wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
  1516.     wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
  1517.     wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
  1518.     return menu;
  1519. }
  1520.  
  1521.  
  1522.  
  1523.  
  1524.  
  1525. /*
  1526.  *---------------------------------------------------------------------- 
  1527.  * configureMenu--
  1528.  *     Reads root menu configuration from defaults database.
  1529.  * 
  1530.  *---------------------------------------------------------------------- 
  1531.  */
  1532. static WMenu*
  1533. configureMenu(WScreen *scr, proplist_t definition)
  1534. {
  1535.     WMenu *menu = NULL;
  1536.     proplist_t elem;
  1537.     int i, count;
  1538.     proplist_t title, command, params;
  1539.     char *tmp, *mtitle;
  1540.  
  1541.     
  1542.     if (PLIsString(definition)) {
  1543.     struct stat stat_buf;
  1544.     char *path = NULL;
  1545.     Bool menu_is_default = False;
  1546.  
  1547.     /* menu definition is a string. Probably a path, so parse the file */
  1548.  
  1549.     tmp = wexpandpath(PLGetString(definition));
  1550.  
  1551.     path = getLocalizedMenuFile(tmp);
  1552.  
  1553.     if (!path)
  1554.         path = wfindfile(DEF_CONFIG_PATHS, tmp);
  1555.  
  1556.     if (!path) {
  1557.         path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
  1558.         menu_is_default = True;
  1559.     }
  1560.  
  1561.     if (!path) {
  1562.         wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
  1563.              tmp);
  1564.         free(tmp);
  1565.         return NULL;
  1566.     }
  1567.  
  1568.     if (stat(path, &stat_buf)<0) {
  1569.         wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
  1570.         free(path);
  1571.         free(tmp);
  1572.         return NULL;
  1573.     }
  1574.  
  1575.     if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
  1576.         /* if the pointer in WMRootMenu has changed */
  1577.         || WDRootMenu->timestamp > scr->root_menu->timestamp) {
  1578.         
  1579.         if (menu_is_default) {
  1580.         wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
  1581.              path);
  1582.         }
  1583.  
  1584.         menu = readMenuFile(scr, path);
  1585.         if (menu)
  1586.         menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
  1587.     } else {
  1588.         menu = NULL;
  1589.     }
  1590.     free(path);
  1591.     free(tmp);
  1592.  
  1593.     return menu;
  1594.     }
  1595.  
  1596.     count = PLGetNumberOfElements(definition);
  1597.     if (count==0)
  1598.     return NULL;
  1599.  
  1600.     elem = PLGetArrayElement(definition, 0);
  1601.     if (!PLIsString(elem)) {
  1602.     tmp = PLGetDescription(elem);
  1603.     wwarning(_("%s:format error in root menu configuration \"%s\""), 
  1604.          "WMRootMenu", tmp);
  1605.     free(tmp);
  1606.     return NULL;
  1607.     }
  1608.     mtitle = PLGetString(elem);
  1609.     
  1610.     menu = wMenuCreate(scr, mtitle, False);
  1611.     menu->on_destroy = removeShortcutsForMenu;
  1612.  
  1613. #ifdef GLOBAL_SUBMENU_FILE
  1614.     {
  1615.     WMenu *submenu;
  1616.     WMenuEntry *mentry;
  1617.  
  1618.     submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
  1619.  
  1620.     if (submenu) {
  1621.         mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
  1622.         wMenuEntrySetCascade(menu, mentry, submenu);
  1623.     }
  1624.     }
  1625. #endif
  1626.  
  1627.     for (i=1; i<count; i++) {
  1628.     elem = PLGetArrayElement(definition, i);
  1629. #if 0
  1630.     if (PLIsString(elem)) {
  1631.         char *file;
  1632.         
  1633.         file = PLGetString(elem);
  1634.         
  1635.     }
  1636. #endif    
  1637.     if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
  1638.         goto error;
  1639.     
  1640.     if (PLIsArray(PLGetArrayElement(elem,1))) {
  1641.         WMenu *submenu;
  1642.         WMenuEntry *mentry;
  1643.         
  1644.         /* submenu */
  1645.         submenu = configureMenu(scr, elem);
  1646.         if (submenu) {
  1647.         mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, 
  1648.                       NULL);
  1649.         wMenuEntrySetCascade(menu, mentry, submenu);
  1650.         }
  1651.     } else {
  1652.         int idx = 0;
  1653.         char *shortcut;
  1654.         /* normal entry */
  1655.  
  1656.         title = PLGetArrayElement(elem, idx++);
  1657.         shortcut = PLGetArrayElement(elem, idx++);
  1658.         if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
  1659.         shortcut = PLGetArrayElement(elem, idx++);
  1660.         command = PLGetArrayElement(elem, idx++);
  1661.         } else {
  1662.         command = shortcut;
  1663.         shortcut = NULL;
  1664.         }
  1665.         params = PLGetArrayElement(elem, idx++);
  1666.  
  1667.         if (!title || !command)
  1668.         goto error;
  1669.         
  1670.         addMenuEntry(menu, PLGetString(title), 
  1671.              shortcut ? PLGetString(shortcut) : NULL, 
  1672.              PLGetString(command),
  1673.              params ? PLGetString(params) : NULL, "WMRootMenu");
  1674.     }
  1675.     continue;
  1676.  
  1677.       error:
  1678.     tmp = PLGetDescription(elem);
  1679.     wwarning(_("%s:format error in root menu configuration \"%s\""), 
  1680.          "WMRootMenu", tmp);
  1681.     free(tmp);
  1682.     }
  1683.     
  1684.     return menu;
  1685. }
  1686.  
  1687.  
  1688.  
  1689.  
  1690.  
  1691.  
  1692.  
  1693.  
  1694. /*
  1695.  *---------------------------------------------------------------------- 
  1696.  * OpenRootMenu--
  1697.  *     Opens the root menu, parsing the menu configuration from the
  1698.  * defaults database.
  1699.  *    If the menu is already mapped and is not sticked to the
  1700.  * root window, it will be unmapped.
  1701.  * 
  1702.  * Side effects:
  1703.  *     The menu may be remade.
  1704.  * 
  1705.  * Notes:
  1706.  * Construction of OPEN_MENU entries are delayed to the moment the
  1707.  * user map's them.
  1708.  *---------------------------------------------------------------------- 
  1709.  */
  1710. void
  1711. OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
  1712. {
  1713.     WMenu *menu=NULL;
  1714.     proplist_t definition;
  1715.     /*
  1716.     static proplist_t domain=NULL;
  1717.     
  1718.     if (!domain) {
  1719.     domain = PLMakeString("WMRootMenu");
  1720.     }
  1721.      */
  1722.     
  1723.     scr->flags.root_menu_changed_shortcuts = 0;
  1724.     scr->flags.added_workspace_menu = 0;
  1725.  
  1726.     if (scr->root_menu && scr->root_menu->flags.mapped) {
  1727.     menu = scr->root_menu;
  1728.     if (!menu->flags.buttoned) {
  1729.         wMenuUnmap(menu);
  1730.     } else {
  1731.         wRaiseFrame(menu->frame->core);
  1732.         
  1733.         if (keyboard)
  1734.         wMenuMapAt(menu, 0, 0, True);
  1735.         else
  1736.         wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
  1737.     }
  1738.     return;
  1739.     }
  1740.     
  1741.     
  1742.     definition = WDRootMenu->dictionary;
  1743.  
  1744. /*
  1745.     definition = PLGetDomain(domain);
  1746.  */
  1747.     if (definition) {
  1748.     if (PLIsArray(definition)) {
  1749.         if (!scr->root_menu 
  1750.         || WDRootMenu->timestamp > scr->root_menu->timestamp) {
  1751.         menu = configureMenu(scr, definition);
  1752.         if (menu)
  1753.             menu->timestamp = WDRootMenu->timestamp;
  1754.  
  1755.         } else
  1756.         menu = NULL;
  1757.     } else {
  1758.         menu = configureMenu(scr, definition);
  1759.     }
  1760.     }
  1761.  
  1762.     if (!menu) {
  1763.     /* menu hasn't changed or could not be read */
  1764.     if (!scr->root_menu) {
  1765.         wMessageDialog(scr, _("Error"),
  1766.               _("The applications menu could not be loaded. "
  1767.                 "Look at the console output for a detailed "
  1768.                 "description of the errors."),
  1769.                _("OK"), NULL, NULL);
  1770.  
  1771.         menu = makeDefaultMenu(scr);
  1772.         scr->root_menu = menu;
  1773.     }
  1774.     menu = scr->root_menu;
  1775.     } else {
  1776.     /* new root menu */
  1777.     if (scr->root_menu)
  1778.         wMenuDestroy(scr->root_menu, True);
  1779.     scr->root_menu = menu;
  1780.     }
  1781.     if (menu) {
  1782.     wMenuMapAt(menu, x-menu->frame->core->width/2, y, keyboard);
  1783.     }
  1784.     
  1785.     if (scr->flags.root_menu_changed_shortcuts)
  1786.     rebindKeygrabs(scr);
  1787. }
  1788.  
  1789. #endif /* !LITE */
  1790.